using System;
using System.Collections.Generic;
using System.Collections;
using System.Collections.Specialized;

// using System.Text;

using System.CodeDom.Compiler;
using Microsoft.CSharp;

using System.Reflection;

namespace GURUCORE.Lib.Core.Reflection
{
    public static class ReflectionHelper
    {

        #region SINGLE OBJECT MANIPULATE
        
        public static object GetPropertyValueOf(object p_ObjectToGet, string p_sPropertyName)
        {
            PropertyInfo property = p_ObjectToGet.GetType().GetProperty(p_sPropertyName);
            return property.GetValue(p_ObjectToGet, null);
        }

        public static void SetPropertyValueOf(object p_ObjectToGet, string p_sPropertyName, object p_Value)
        {            
            PropertyInfo property = p_ObjectToGet.GetType().GetProperty(p_sPropertyName);
            property.SetValue(p_ObjectToGet, p_Value, null);
        }

        
        /// <summary>
        /// Get all properties values of an object, save to the hashtable
        /// </summary>
        /// <param name="p_GetFromObject">object to get</param>
        /// <returns>Hashtable, key is property name, value is property value. The hashtable is case-IN sensitive</returns>
        public static HybridDictionary GetAllPropertyValues(object p_GetFromObject)
        {
            HybridDictionary retDic = new HybridDictionary(true);
            PropertyInfo[] arrProp = p_GetFromObject.GetType().GetProperties();
            foreach (PropertyInfo prop in arrProp)
            {
                retDic.Add(prop.Name, prop.GetValue(p_GetFromObject, null));
            }
            return retDic;
        }

        #endregion

        #region SINGLE DTO MANIPULATE

        public static HybridDictionary GetAllDTOFieldValues(object p_object)
        {
            HybridDictionary retDic = new HybridDictionary(true);
            PropertyInfo[] arrProp = p_object.GetType().GetProperties();
            foreach (PropertyInfo prop in arrProp)
            {
                //if(prop.GetCustomAttributes(typeof(GURUCORE.Framework.Core.Data.PersistencePropertyAttribute),)
                //retDic.Add(prop.Name, prop.GetValue(p_GetFromObject, null));
            }
            return retDic;
        }

        #endregion

        /// <summary>
        /// This function is IN-Sensitive
        /// </summary>
        /// <param name="p_sTypeName">string name type</param>
        /// <returns>Type object, null if can not found, IN-sensitive </returns>
        public static Type GetTypeFromName(string p_sTypeName)
        {
            return Type.GetType(p_sTypeName, false, true);
        }

        public static object CreateInstanceByName(string p_sTypeName, params object[] p_arrArgs)
        {
            return Activator.CreateInstance(GetTypeFromName(p_sTypeName), p_arrArgs);
        }

        /// <summary>
        /// This function is PERFORMANCE hit, because of .NET. Don't use without deep understanding. It's 46times slower
        /// </summary>
        /// <param name="p_sGenericTypeName"></param>
        /// <param name="p_arrsGenericParameterTypeName"></param>
        /// <returns></returns>
        public static object CreateInstanceGenericByName(string p_sGenericTypeName, params string[] p_arrsGenericParameterTypeName)
        {
            const string GENERIC_CLR_SIGNAL = "`";
            string sGenericParameterTypeNameList = string.Empty;
            foreach (string s in p_arrsGenericParameterTypeName)
            {
                sGenericParameterTypeNameList += s + ",";
            }
            sGenericParameterTypeNameList = sGenericParameterTypeNameList.TrimEnd(',');

            string sTypeName = p_sGenericTypeName + 
                GENERIC_CLR_SIGNAL + p_arrsGenericParameterTypeName.Length + 
                "[" + sGenericParameterTypeNameList + "]";
            return CreateInstanceByName(sTypeName);
        }

        /// <summary>
        /// This function is PERFORMANCE hit, because of .NET. Don't use without deep understanding. It's 46times slower
        /// </summary>
        /// <param name="p_GenericType"></param>
        /// <param name="p_arrGenericParameterTypeName"></param>
        /// <returns></returns>
        public static object CreateInstanceGeneric(Type p_GenericType, params Type[] p_arrGenericParameterTypeName)
        {
            return Activator.CreateInstance(p_GenericType.MakeGenericType(p_arrGenericParameterTypeName));
        }




        public static object InvokeMethod(object p_ObjectToRun, string p_sMethodName, object[] parameters)
        {
            return p_ObjectToRun.GetType().GetMethod(p_sMethodName).Invoke(p_ObjectToRun, parameters);
        }

        /// <summary>
        /// This function is PERFORMANCE hit, because of .NET. Don't use without deep understanding. It's 149times slower
        /// </summary>
        /// <param name="p_ObjectToRun"></param>
        /// <param name="p_sMethodName"></param>
        /// <param name="parameters">parameter to pass to function calling</param>
        /// <param name="p_arrGenericMakeTypes">array of type to pass to the the generic function</param>
        /// <returns></returns>
        public static object InvokeMethodGeneric(object p_ObjectToRun, string p_sMethodName, object[] parameters, params Type[] p_arrGenericMakeTypes)
        {
            return p_ObjectToRun.GetType().GetMethod(p_sMethodName).MakeGenericMethod(p_arrGenericMakeTypes).Invoke(p_ObjectToRun, parameters);
        }



        /// <summary>
        /// Thachnn 30/Sep/2005
        /// Compile C# source code file to DLL asemblly.
        /// If Compile SUCCESS, this function will return the DLL file fullpath
        /// DLL filename is C# file name (without .cs extension) + .pstrDLL_EXTENSION
        /// throw exception if has error.
        /// </summary>
        /// <param name="pstrCSharpFilePath">C# file path to  compile. You should provide full path</param>
        /// <param name="pstrDLL_EXTENSION">extension of DLL file name, Ex: .DLL</param>
        /// <param name="pstrSYSTEM_ASSEMBLY">System Assembly name. Ex: System.dll</param>
        /// <param name="pstrSYSTEM_DATA_ASSEMBLY">System.Data Assembly name. Ex: System.Data.dll</param>
        /// <param name="pstrSYSTEM_XML_ASSEMBLY">System.Xml Assembly name. Ex: System.Xml.dll</param>
        /// <param name="pstrCOMMAND_SEPERATOR">seperator sign. Use only in return error message</param>
        /// <returns>Compiled DLL file path (fullpath) of the input C# source file. NULL if error</returns>
        /// If you know exactly, put it here. like "System.EnterpriseServices.dll";
        /// If you know the class, use this syntax  ///   string C1PPV = typeof(C1.Win.C1PrintPreview.C1PrintPreview).Assembly.Location;
        public static string CompileCSharpFile(
            string pstrCSharpFilePath,
            string p_sFolderToStoreDLLResult,
            string pstrDLL_EXTENSION,
            params string[] p_arrReferenceWhileCompileAssemblyFileName)
        {
            // include the utc time to avoid share file conflict. Later, delete this file after done			
            string strResultAssemblyName =
                "__" + System.IO.Path.GetFileNameWithoutExtension(pstrCSharpFilePath) +
                Text.TextHelper.NowToUTCString() + pstrDLL_EXTENSION;

            /// this folder should have write permission
            // string strFolder = System.IO.Path.GetDirectoryName(pstrCSharpFilePath);
            string strFolder = p_sFolderToStoreDLLResult;


            // create new Compiler to compile the file before create another AppDomain
            // ICodeCompiler objCompiler = new CSharpCodeProvider().CreateCompiler();
            CompilerParameters objParamters = new CompilerParameters();
            // start by adding any referenced assemblies
            foreach (string sReferenceAssemblyFile in p_arrReferenceWhileCompileAssemblyFileName)
            {

                objParamters.ReferencedAssemblies.Add(sReferenceAssemblyFile);
            }

            objParamters.GenerateInMemory = true;	// load the resulting assembly into memory
            objParamters.OutputAssembly = p_sFolderToStoreDLLResult + "\\" + strResultAssemblyName;	// compile the file to the CSharp file's folder

            // now compile the whole thing
            CompilerResults objCompiled = new CSharpCodeProvider().CompileAssemblyFromFile(objParamters, pstrCSharpFilePath);

            // if compile error then display the error message and return
            if (objCompiled.Errors.HasErrors)
            {
                string strErrorMsg = string.Empty;
                // create error string
                strErrorMsg = objCompiled.Errors.Count.ToString() + " ERRORs";
                for (int i = 0; i < objCompiled.Errors.Count; i++)
                {
                    strErrorMsg = strErrorMsg + Environment.NewLine + 
                        "Line:" + objCompiled.Errors[i].Line + "-----" + objCompiled.Errors[i].ErrorText;
                }
                throw new Exception(string.Format("Can't compile C# file <{0}>, >>> {1}", pstrCSharpFilePath, strErrorMsg));
            }

            return p_sFolderToStoreDLLResult + "\\" + strResultAssemblyName;
        }

    }
}
